/**
 * Copyright 2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.xume.raob.extensions.jaxb;

import java.io.File;
import java.io.OutputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

/**
 * @author Johan Siebens
 */
final class JaxbOperations {

	private static final Map<String, JAXBContext> JAXB_CONTEXTS = new ConcurrentHashMap<String, JAXBContext>();

	static String marshal(Object object) {
		try {
			StringWriter writer = new StringWriter();
			marshaller(object).marshal(object, writer);
			return writer.toString();
		} catch (JAXBException e) {
			throw marshalFailure(object, e);
		}
	}

	static void marshal(Object object, File output) {
		try {
			marshaller(object).marshal(object, output);
		} catch (JAXBException e) {
			throw marshalFailure(object, e);
		}
	}

	static void marshal(Object object, OutputStream os) {
		try {
			marshaller(object).marshal(object, os);
		} catch (JAXBException e) {
			throw marshalFailure(object, e);
		}
	}

	static void marshal(Object object, Writer writer) {
		try {
			marshaller(object).marshal(object, writer);
		} catch (JAXBException e) {
			throw marshalFailure(object, e);
		}
	}

	static <T> T unmarshal(String xml, Class<T> targetClass) {
		try {
			StringReader reader = new StringReader(xml);
			return targetClass.cast(unmarshaller(targetClass).unmarshal(reader));
		} catch (JAXBException e) {
			throw new JaxbBuilderException("Failed to unmarshal xml to instance of [" + targetClass + "]", e);
		}
	}

	private static JAXBContext jaxbContext(Class<? extends Object> targetClass) throws JAXBException {
		String packageName = targetClass.getPackage().getName();
		JAXBContext jaxbContext = JAXB_CONTEXTS.get(packageName);
		if (jaxbContext == null) {
			jaxbContext = JAXBContext.newInstance(packageName);
			JAXB_CONTEXTS.put(packageName, jaxbContext);
		}
		return jaxbContext;
	}

	private static JaxbBuilderException marshalFailure(Object object, JAXBException e) {
		return new JaxbBuilderException("Failed to marshal instance of [" + object.getClass() + "]", e);
	}

	private static Marshaller marshaller(Object object) throws JAXBException {
		Class<? extends Object> targetClass = object.getClass();
		return jaxbContext(targetClass).createMarshaller();
	}

	private static Unmarshaller unmarshaller(Class<?> targetClass) throws JAXBException {
		return jaxbContext(targetClass).createUnmarshaller();
	}

	private JaxbOperations() {
	}

}
